// Copyright (c) 2008-2010, Arne Claus
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 
// following conditions are met:
//
// - Redistributions of source code must retain the above copyright notice, this list of conditions and the following 
//   disclaimer.
// - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
//   disclaimer in the documentation and/or other materials provided with the distribution.
// - Neither the name Arne Claus nor the names of any contributors may be used to endorse or promote products
//   derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef __INCLUDED_CBL_LOCKS_H__
#define __INCLUDED_CBL_LOCKS_H__

#include "CBL_Defines.h"
#ifdef CB_WINDOWS
#include "CBL_Windows.h"
#else
#include <pthread.h>
#endif

CB_SUBNAMESPACE_START(Cpu)

// ---------------------------------------------------------------------------------
//  Mutex wrapper class
// ---------------------------------------------------------------------------------

//! \class  CMutex
//! \detail Implementation of a standard mutex.
class CMutex
{
public:

	enum EMutexType
    {
    	Default = 0,	//!< See PTHREAD_MUTEX_RECURSIVE
        ErrorChecked,	//!< See PTHREAD_MUTEX_ERRORCHECK, same as Default on Windows
        NonRecursive,	//!< See PTHREAD_MUTEX_NORMAL, same as Default on Windows
        NumLockTypes
    };
    
public:

	CMutex(EMutexType _Type=Default);
    ~CMutex();
    
    //! \brief Acquire a read lock, wait if already locked.
    void Lock();
    
    //! \brief Acquire a read lock, continue if already locked.
    bool TryLock();
    
    //! \brief Release the lock.
    bool Unlock();

private:
#ifdef CB_WINDOWS
	CRITICAL_SECTION m_Mutex;
#else
	pthread_mutex_t m_MutexId;
#endif    
};

// ---------------------------------------------------------------------------------
//  Read/Write lock wrapper class
// ---------------------------------------------------------------------------------

//! \class  CRWLock
//! \detail Implementation of a read/write lock object.
class CRWLock
{
public:

	CRWLock();
    ~CRWLock();
    
    //! \brief Acquire a read lock, wait if already locked.
    void ReadLock();
    
    //! \brief  Acquire a read lock, continue if already locked.
	//! \detail Same as ReadLock() under Windows
    bool TryReadLock();
    
    //! \brief Acquire a write lock, wait if already locked.
    void WriteLock();
	
    //! \brief  Acquire a write lock, continue if already locked.
	//! \detail Same as WriteLock() under Windows
    bool TryWriteLock();
	
	//! \brief Unlocks the ReadLock() and calls WriteLock(), thus staying unlocked on error.
	void UpgradeLock();
	
    //! \brief Unlocks the ReadLock() and calls TryWriteLock(), restoring ReadLock() on fail.
	bool TryUpgradeLock();
    
    //! \brief Release the current lock.
    bool Unlock();

private:
#ifdef CB_WINDOWS
	PSRWLOCK m_pLock;
	enum EType { None, Shared, Exclusive };
	EType m_LockType;
#else
	pthread_rwlock_t m_LockId;
#endif
};

// ---------------------------------------------------------------------------------
//  Condition variable wrapper class
// ---------------------------------------------------------------------------------

//! \class  CCondition
//! \detail Implementation of a condition variable object.
class CCondition
{
public:
    
	CCondition();
    ~CCondition();
    
    //! \brief Wait for the condition to be signaled
    void Wait();
    
    //! \brief Signal the current thread to wake up on this condition
    void Signal();
    
    //! \brief Signal all threads to wake up on this condition
    void Broadcast();
    
private:
#ifdef CB_WINDOWS
	CONDITION_VARIABLE m_Condition;
	CRITICAL_SECTION   m_Mutex;
#else
	pthread_cond_t  m_ConditionId;
    pthread_mutex_t m_MutexId;
#endif
};

// ---------------------------------------------------------------------------------
//  Critical section helper
// ---------------------------------------------------------------------------------

//! /class  CCriticalSection
//! /detail A scope based helper class for using a given mutex for critical section control
//!		    The lock will be acquired on creation and released upon destruction, e.g. when
//!		    the object goes out of scope, so it can be used with return values, too.
class CCriticalSection
{
public:

	CCriticalSection(CMutex& _rMutex) : m_pMutex(&_rMutex)	{ m_pMutex->Lock(); }
    ~CCriticalSection() 									{ m_pMutex->Unlock(); }
    
private:

	CMutex* m_pMutex;
};

//! \brief  Helper function for doing a test inside a ciritcal section, using a given
//!			mutex as controler.
CB_FORCEINLINE bool CB_INLINE_ATTR CriticalSectionTest(CMutex& _rMutex, bool _Test)
{
	CCriticalSection CriticalSectionScope(_rMutex);
    return _Test;
}

// Convenience macros for java style synchronized blocks

#define CB_CRITICAL_SECTION_GUARD(_Mutex) CB_SNS(Cpu)::CCriticalSection CBCriticalSectionScope(_Mutex);
#define CB_CRITICAL_SECTION_START(_Mutex) { CB_CRITICAL_SECTION_GUARD(_Mutex);
#define CB_CRITICAL_SECTION_END }

CB_SUBNAMESPACE_END
#endif
